package com.papper.common.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * MyBeanUtil
 * <h5>描述:</h5>
 */
public class MyBeanUtil extends BeanUtils {
	private static final String PREFIX_GET    = "get";
	private static final String GETCLASS_NAME = "getClass";

	// key:clazz.getName();
	private static Map<String, List<Method>> objGetMethods = new ConcurrentHashMap<>(16);

	/**
	 * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中<br>
	 * 注意:当第一个参数中的属性有null值时,不会拷贝进去
	 *
	 * @param source 源对象
	 * @param target 目标对象
	 * @throws BeansException
	 */
	public static void copyPropertiesIgnoreNull(Object source, Object target) throws BeansException {
		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");
		Class<?>             actualEditable = target.getClass();
		PropertyDescriptor[] targetPds      = getPropertyDescriptors(actualEditable);
		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null) {
					try {
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);
						// 这里判断value是否为空 当然这里也能进行一些特殊要求的处理
						// 例如绑定时格式转换等等
						if (value != null) {
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(target, value);
						}
					} catch (Throwable ex) {
						throw new FatalBeanException("Could not copy properties from source to target field name mismatch:" + targetPd.getName(), ex);
					}
				}
			}
		}
	}


	/**
	 * 属性拷贝，把map中的值拷贝到target中去
	 *
	 * @param map    map对象
	 * @param target 目标对象
	 */
	public static void copyPropertiesForMap(Map<String, Object> map, Object target) {
		Assert.notNull(map, "map must not be null");
		Assert.notNull(target, "Target must not be null");
		Object pojo = mapToPojo(map, target.getClass());
		copyProperties(pojo, target);
	}


	/**
	 * 深层次拷贝，通过json转换的方式实现
	 *
	 * @param from    待转换的集合类
	 * @param toClass 目标类class
	 * @param <T>     目标类
	 * @return 返回目标类
	 */
	public static <T> List<T> deepCopy(List<?> from, Class<T> toClass) {
		if (from == null || from.isEmpty()) {
			return new ArrayList<>();
		}
		String json = JSON.toJSONString(from, SerializerFeature.WriteDateUseDateFormat);
		return JSONArray.parseArray(json, toClass);
	}

	/**
	 * 深层次拷贝，通过json转换的方式实现
	 *
	 * @param from    待转换的类
	 * @param toClass 目标类class
	 * @param <T>     目标类
	 * @return 返回目标类
	 */
	public static <T> T deepCopy(Object from, Class<T> toClass) {
		if (from == null) {
			return null;
		}
		String json = JSON.toJSONString(from, SerializerFeature.WriteDateUseDateFormat);
		return JSON.parseObject(json, toClass);
	}

	/**
	 * 将实体对象转换成Map
	 *
	 * @param pojo 实体类
	 * @return 返回map
	 */
	public static Map<String, Object> pojoToMap(Object pojo) {
		if (pojo == null) {
			return Collections.emptyMap();
		}
		Method[]            methods = pojo.getClass().getDeclaredMethods();
		Map<String, Object> map     = new HashMap<String, Object>(methods.length);

		try {
			for (Method method : methods) {
				String methodName = method.getName();

				if (methodName.startsWith(PREFIX_GET) && method.getParameterTypes().length == 0) {
					String fieldName = buildFieldName(methodName);
					Object value     = method.invoke(pojo);
					map.put(fieldName, value);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("pojoToMap失败", e);
		}

		return map;
	}

	// 构建列名
	private static String buildFieldName(String methodName) {
		return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
	}

	/**
	 * 是否是get方法
	 */
	public static boolean isGetMethod(Method method) {
		if (method.getReturnType() == Void.TYPE) {
			return false;
		}
		String methodName = method.getName();
		return (!GETCLASS_NAME.equals(methodName)) && methodName.startsWith(PREFIX_GET);
	}

	/**
	 * 将map对象转换成普通类
	 *
	 * @param <T>       普通类类型
	 * @param map       map对象
	 * @param pojoClass 普通类
	 * @return 返回普通类
	 */
	public static <T> T mapToPojo(Map<String, ?> map, Class<T> pojoClass) {
		return JSON.parseObject(JSON.toJSONString(map), pojoClass);
	}

	/**
	 * 转换对象中的某个值
	 *
	 * @param <T>      普通类类型
	 * @param map      map对象
	 * @param valClass 普通类
	 * @param key      值对应的key
	 * @return 返回单值，没有返回null
	 */
	public static <T> T mapToValue(Map<String, Object> map, Class<T> valClass, String key) {
		Object val = map.get(key);
		if (val == null) {
			return null;
		}
		if (valClass == String.class) {
			return (T) String.valueOf(val);
		}
		return JSON.parseObject(String.valueOf(val), valClass);
	}

	/**
	 * map集合转换成对象集合
	 *
	 * @param <T>       普通类类型
	 * @param list      map集合
	 * @param pojoClass 待转换的对象类型
	 * @return 返回对象集合
	 */
	public static <T> List<T> mapListToObjList(List<Map<String, Object>> list, Class<T> pojoClass) {
		if (list == null) {
			return Collections.emptyList();
		}
		List<T> retList = new ArrayList<>(list.size());
		for (Map<String, Object> map : list) {
			retList.add(mapToPojo(map, pojoClass));
		}
		return retList;
	}

	/**
	 * map集合转换成单值集合
	 *
	 * @param <T>      普通类类型
	 * @param list     map集合
	 * @param valClass 待转换的对象类型
	 * @return 返回单值集合，没有返回
	 */
	public static <T> List<T> mapListToValueList(List<Map<String, Object>> list, Class<T> valClass, String key) {
		if (list == null) {
			return Collections.emptyList();
		}
		List<T> retList = new ArrayList<>(list.size());
		for (Map<String, Object> map : list) {
			T value = mapToValue(map, valClass, key);
			if (value != null) {
				retList.add(value);
			}
		}
		return retList;
	}

	public static <T> T parseValue(Object value, Class<T> valClass) {
		if (valClass == Object.class) {
			return (T) value;
		}
		if (valClass == String.class) {
			return (T) String.valueOf(value);
		}
		return JSON.parseObject(String.valueOf(value), valClass);
	}
}
